package com.tourscool.springboot.starter.inquiry.core;

import com.tourscool.springboot.starter.inquiry.utils.NetworkUtils;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.WebApplicationContext;

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Cedric
 * @date 2020/10/28 09:53
 * @description 获取RequestMapping注解的方法列表
 */
public final class RequestMappingUtils {

    /**
     * 获取指定文件下面的RequestMapping方法保存在map中
     *
     * @return 执行方法Map
     * @param applicationContext 应用上下文
     */
    public static List<RequestMethodItem> getRequestMappingMethodList(ApplicationContext applicationContext) {
        List<RequestMethodItem> requestMethodItemList = new ArrayList<>();

        WebApplicationContext appContext = (WebApplicationContext) applicationContext;

        if (null == appContext) {
            return requestMethodItemList;
        }

        String serverName = NetworkUtils.getIp().getIP();
        Environment environment = appContext.getBean(Environment.class);
        String cServiceName = environment.getProperty("spring.application.name", "unknown");

        Map<String, Object> beansWithAnnotation = appContext.getBeansWithAnnotation(SpringBootApplication.class);
        Class<?> mainClass = ClassUtils.getUserClass(beansWithAnnotation.values().toArray()[0].getClass());
        RequestScanPackage requestScanPackage = mainClass.getAnnotation(RequestScanPackage.class);
        if (requestScanPackage == null) {
            return requestMethodItemList;
        }
        Reflections reflections = new Reflections((Object) requestScanPackage.value());
        Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(FeignClient.class);

        for (Class<?> clazz : classesList) {
            FeignClient feignClient = clazz.getAnnotation(FeignClient.class);
            String pServiceName = StringUtils.isNotEmpty(feignClient.name()) ? feignClient.name() : feignClient.value();
            //得到该类下面的所有方法
            Method[] methods = clazz.getDeclaredMethods();

            for (Method method : methods) {
                //得到该类下面的RequestMapping注解
                GetMapping getMapping = method.getAnnotation(GetMapping.class);
                PutMapping putMapping = method.getAnnotation(PutMapping.class);
                PostMapping postMapping = method.getAnnotation(PostMapping.class);
                DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
                RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                String controllerName = clazz.getName();
                String requestMethodName = method.getName();
                Class<?>[] methodParamTypes = method.getParameterTypes();
                Class<?> returnType = method.getReturnType();
                String methodParamType = Arrays.stream(methodParamTypes).map(Class::getName).collect(Collectors.joining(","));
                if (getMapping != null) {
                    RequestMethodItem requestMethodItem = new RequestMethodItem(serverName, pServiceName, controllerName, requestMethodName,
                            RequestMethod.GET.name(), getMapping.value()[0], cServiceName, methodParamType, returnType.getName());
                    requestMethodItemList.add(requestMethodItem);
                }
                if (putMapping != null) {
                    RequestMethodItem requestMethodItem = new RequestMethodItem(serverName, pServiceName, controllerName, requestMethodName,
                            RequestMethod.PUT.name(), putMapping.value()[0], cServiceName,methodParamType, returnType.getName());
                    requestMethodItemList.add(requestMethodItem);
                }
                if (postMapping != null) {
                    RequestMethodItem requestMethodItem = new RequestMethodItem(serverName, pServiceName, controllerName, requestMethodName,
                            RequestMethod.POST.name(), postMapping.value()[0], cServiceName, methodParamType, returnType.getName());
                    requestMethodItemList.add(requestMethodItem);
                }
                if (deleteMapping != null) {
                    RequestMethodItem requestMethodItem = new RequestMethodItem(serverName, pServiceName, controllerName, requestMethodName,
                            RequestMethod.DELETE.name(), deleteMapping.value()[0], cServiceName,methodParamType, returnType.getName());
                    requestMethodItemList.add(requestMethodItem);
                }
                if (requestMapping != null) {
                    RequestMethodItem requestMethodItem = new RequestMethodItem(serverName, pServiceName, controllerName, requestMethodName,
                            StringUtils.EMPTY, requestMapping.value()[0], cServiceName, methodParamType, returnType.getName());
                    requestMethodItemList.add(requestMethodItem);
                }
            }
        }
        return requestMethodItemList;
    }

}
